﻿#region Imports
using System;
using System.Management;
using System.Text.RegularExpressions;
using CommonUtilities.Enums;
using CommonUtilities.Exceptions;
using CommonUtilities.Extensions;
#endregion

namespace CommonUtilities.Enums
{
    public enum eWindowsVersion
    {
        WinXp_32,
        WinXp_64,
        Win7_32,
        Win7_64,
        Win8_32,
        Win8_64,
    }
}

namespace CommonUtilities.Util
{
    /// <summary>
    /// 
    /// </summary>
    /// <seealso cref="http://andrewensley.com/2009/06/c-detect-windows-os-part-1/"/>
    /// <seealso cref="http://support.microsoft.com/kb/304283"/>
    /// <seealso cref="http://code.msdn.microsoft.com/windowsdesktop/CSCheckOSVersion-681dbd2a"/>
    /// <seealso cref="http://www.codeproject.com/Articles/1473/Detecting-the-Operating-System-Version"/>
    public class OSVersion
    {
        public eWindowsVersion WindowsVersion { get; private set; }

        public OSVersion()
        {
            string s1 = Environment.CommandLine;
            string s2 = Environment.CurrentDirectory;
            Version s3 = Environment.OSVersion.Version;
            string s4 = Environment.OSVersion.VersionString;
            int bits = GetOSArchitectureLegacy();
            string osInfo = GetOSInfo();
            string osInfo2 = GetOSInfoWithWMI();
            string osLegacy = GetOSLegacy();
            string osSPLegacy = GetOSServicePackLegacy();
        }

        /// <summary>
        /// Gets Operating System Architecture.  This does not tell you if the program in running in
        /// 32- or 64-bit mode or if the CPU is 64-bit capable.  It tells you whether the actual Operating
        /// System is 32- or 64-bit.
        /// </summary>
        /// <returns>Int containing 32 or 64 representing the number of bits in the OS Architecture</returns>
        int GetOSArchitectureLegacy()
        {
            string pa = Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
            return ((pa.IsNullOrEmpty() || String.Compare(pa, 0, "x86", 0, 3, true) == 0) ? 32 : 64);

            return Environment.Is64BitOperatingSystem ? 64 : 32;
        }

        int GetProgramBits()
        {
            return IntPtr.Size * 8;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <seealso cref="http://msdn.microsoft.com/en-us/library/windows/desktop/ms724832%28v=vs.85%29.aspx"/>
        /// <returns></returns>
        string GetOSInfo()
        {
            OperatingSystem os = Environment.OSVersion; //Get Operating system information
            Version vs = os.Version; //Get version information about the OS

            string operatingSystem = ""; //Variable to hold our return value

            if (os.Platform == PlatformID.Win32Windows)
            {
                //This is a pre-NT version of Windows
                switch (vs.Minor)
                {
                    case 0:
                        operatingSystem = "95";
                        break;
                    case 10:
                        if (vs.Revision.ToString() == "2222A")
                            operatingSystem = "98SE";
                        else
                            operatingSystem = "98";
                        break;
                    case 90:
                        operatingSystem = "Me";
                        break;
                    default:
                        break;
                }
            }
            else if (os.Platform == PlatformID.Win32NT)
            {
                switch (vs.Major)
                {
                    case 3:
                        operatingSystem = "NT 3.51";
                        break;

                    case 4:
                        operatingSystem = "NT 4.0";
                        break;

                    case 5:
                        if (vs.Minor == 0) operatingSystem = "2000"; // Windows 2000
                        else if (vs.Minor == 1) operatingSystem = "XP"; // Windows XP
                        else if (vs.Minor == 2) operatingSystem = "XP (64-bit)"; // Windows XP 64-Bit Edition; Windows Server 2003; Windows Server 2003 R2
                        else throw new OSVersionInfoNotAvailableException("Missing parse info. Update code.");
                        break;

                    case 6:
                        if (vs.Minor == 0) operatingSystem = "Vista"; // Windows Vista; Windows Server 2008
                        else if (vs.Minor == 1) operatingSystem = "7"; // Windows 7; Windows Server 2008 R2
                        else if (vs.Minor == 2) operatingSystem = "8"; // Windows 8; Windows Server 2012
                        else if (vs.Minor == 3) operatingSystem = "8.1"; // Windows 8.1; Windows Server 2012 R2
                        else throw new OSVersionInfoNotAvailableException("New version available. Update code.");
                        break;

                    default:
                        throw new OSVersionInfoNotAvailableException("New version available. Update code.");
                }
            }

            //Make sure we actually got something in our OS check
            //We don't want to just return " Service Pack 2" or " 32-bit"
            //That information is useless without the OS version.
            if (operatingSystem != "")
            {
                //Got something.  Let's prepend "Windows" and get more info.
                operatingSystem = "Windows " + operatingSystem;
                //See if there's a service pack installed.
                if (os.ServicePack != "")
                {
                    //Append it to the OS name.  i.e. "Windows XP Service Pack 3"
                    operatingSystem += " " + os.ServicePack;
                }
                //Append the OS architecture.  i.e. "Windows XP Service Pack 3 32-bit"
                operatingSystem += " " + GetOSArchitectureLegacy().ToString() + "-bit";
            }
            //Return the information we've gathered.
            return operatingSystem;
        }

        #region WMI
        /// <summary>
        /// Gets Operating System Name, Service Pack, and Architecture using WMI with the legacy methods as a fallback
        /// </summary>
        /// <returns>String containing the name of the operating system followed by its service pack (if any) and architecture</returns>
        private string GetOSInfoWithWMI()
        {
            ManagementObjectSearcher objMOS = new ManagementObjectSearcher("SELECT * FROM  Win32_OperatingSystem");

            //Variables to hold our return value
            string os = "";
            int OSArch = 0;

            try
            {
                foreach (ManagementObject objManagement in objMOS.Get())
                {
                    // Get OS version from WMI - This also gives us the edition
                    object osCaption = objManagement.GetPropertyValue("Caption");
                    if (osCaption != null)
                    {
                        // Remove all non-alphanumeric characters so that only letters, numbers, and spaces are left.
                        string osC = Regex.Replace(osCaption.ToString(), "[^A-Za-z0-9 ]", "");
                        //string osC = osCaption.ToString();
                        // If the OS starts with "Microsoft," remove it.  We know that already
                        if (osC.StartsWith("Microsoft"))
                        {
                            osC = osC.Substring(9);
                        }
                        // If the OS now starts with "Windows," again... useless.  Remove it.
                        if (osC.Trim().StartsWith("Windows"))
                        {
                            osC = osC.Trim().Substring(7);
                        }
                        // Remove any remaining beginning or ending spaces.
                        os = osC.Trim();
                        // Only proceed if we actually have an OS version - service pack is useless without the OS version.
                        if (!String.IsNullOrEmpty(os))
                        {
                            object osSP = null;
                            try
                            {
                                // Get OS service pack from WMI
                                osSP = objManagement.GetPropertyValue("ServicePackMajorVersion");
                                if (osSP != null && osSP.ToString() != "0")
                                {
                                    os += " Service Pack " + osSP.ToString();
                                }
                                else
                                {
                                    // Service Pack not found.  Try built-in Environment class.
                                    os += GetOSServicePackLegacy();
                                }
                            }
                            catch (Exception)
                            {
                                // There was a problem getting the service pack from WMI.  Try built-in Environment class.
                                os += GetOSServicePackLegacy();
                            }
                        }
                        object osA = null;
                        try
                        {
                            // Get OS architecture from WMI
                            osA = objManagement.GetPropertyValue("OSArchitecture");
                            if (osA != null)
                            {
                                string osAString = osA.ToString();
                                // If "64" is anywhere in there, it's a 64-bit architectore.
                                OSArch = (osAString.Contains("64") ? 64 : 32);
                            }
                        }
                        catch (Exception)
                        {
                        }
                    }
                }
            }
            catch (Exception)
            {
            }
            // If WMI couldn't tell us the OS, use our legacy method.
            // We won't get the exact OS edition, but something is better than nothing.
            if (os == "")
            {
                os = GetOSLegacy();
            }
            // If WMI couldn't tell us the architecture, use our legacy method.
            if (OSArch == 0)
            {
                OSArch = GetOSArchitectureLegacy();
            }
            return os + " " + OSArch.ToString() + "-bit";
        }
        #endregion

        #region Legacy code
        /// <summary>
        /// Gets Operating System Name using .Net's Environment class.
        /// </summary>
        /// <returns>String containing the name of the operating system followed by its service pack (if any)</returns>
        private string GetOSLegacy()
        {
            //Get Operating system information.
            OperatingSystem os = Environment.OSVersion;
            //Get version information about the os.
            Version vs = os.Version;

            //Variable to hold our return value
            string operatingSystem = "";

            if (os.Platform == PlatformID.Win32Windows)
            {
                //This is a pre-NT version of Windows
                switch (vs.Minor)
                {
                    case 0:
                        operatingSystem = "95";
                        break;
                    case 10:
                        if (vs.Revision.ToString() == "2222A")
                            operatingSystem = "98SE";
                        else
                            operatingSystem = "98";
                        break;
                    case 90:
                        operatingSystem = "Me";
                        break;
                    default:
                        break;
                }
            }
            else if (os.Platform == PlatformID.Win32NT)
            {
                switch (vs.Major)
                {
                    case 3:
                        operatingSystem = "NT 3.51";
                        break;
                    case 4:
                        operatingSystem = "NT 4.0";
                        break;
                    case 5:
                        if (vs.Minor == 0)
                        {
                            operatingSystem = "2000";
                        }
                        else
                        {
                            operatingSystem = "XP";
                        }
                        break;
                    case 6:
                        if (vs.Minor == 0)
                        {
                            operatingSystem = "Vista";
                        }
                        else
                        {
                            operatingSystem = "7";
                        }
                        break;
                    default:
                        break;
                }
            }
            //Make sure we actually got something in our OS check
            //We don't want to just return " Service Pack 2"
            //That information is useless without the OS version.
            if (operatingSystem != "")
            {
                //Got something.  Let's see if there's a service pack installed.
                operatingSystem += GetOSServicePackLegacy();
            }
            //Return the information we've gathered.
            return operatingSystem;
        }

        /// <summary>
        /// Gets the installed Operating System Service Pack using .Net's Environment class.
        /// </summary>
        /// <returns>String containing the operating system's installed service pack (if any)</returns>
        private string GetOSServicePackLegacy()
        {
            // Get service pack from Environment Class
            string sp = Environment.OSVersion.ServicePack;
            if (sp != null && sp.ToString() != "" && sp.ToString() != " ")
            {
                // If there's a service pack, return it with a space in front (for formatting)
                return " " + sp.ToString();
            }
            // No service pack.  Return an empty string
            return "";
        }
        #endregion
    }
}
